//管理目标元素外点击事件的 Hook
// onClickAway	触发事件的函数	(event) => void	-
// target	DOM 节点或者 Ref 对象，支持数组	Target | Target[]	-
// eventName 指定需要监听的事件 string
//ref有一个类型声明为React.RefObject<HTMLDivElement>
//https://www.jianshu.com/p/2c40a570d738?utm_campaign=haruki&utm_content=note&utm_medium=seo_notes&utm_source=recommendation
import {RefObject,useEffect ,useRef} from "react"

const defaultEvents = ['mousedown', 'touchstart']

//https://blog.csdn.net/themagickeyjianan/article/details/87890307
//https://blog.csdn.net/yehuozhili/article/details/106396729
//参数要展开(...args)
export const on = (obj:any,...args:any[])=> obj.addEventListener(...args)
export const off = (obj:any,...args:any[]) => obj.removeEventListener(...args)

const useClickAway = <E extends Event = Event>(
  ref:RefObject<HTMLElement | null>,
  onClickAway:(event:E)=> void,
  events:string[] = defaultEvents
)=>{
  //https://zhuanlan.zhihu.com/p/117577458
  //https://coding.imooc.com/learn/questiondetail/194075.html
  //保存数据用useRef,传入默认值来指定默认值，需要使用的时候，访问 ref.current 即可访问到组件实例
  const savedCallback= useRef(onClickAway)

  useEffect(()=>{
    savedCallback.current = onClickAway
  },[onClickAway])
  //js的contains方法用来查看dom元素的包含关系
  //event.target 属性返回哪个 DOM 元素触发了事件。
  useEffect(()=>{
    
    const handler = (event:E)=>{
      const { current :el}  = ref
      //https://blog.csdn.net/lichwei1983/article/details/43897881
        // 判断点击元素是否在el外
        // 由于ev.target的类型是EventTarget,
        // 而contains方法标注的参数类型是Node, 
        // 实际上EventTarget也是dom元素,
        // 所以此处使用需要类型断言, 标注为Node类型
      el && !el.contains(event.target as Node) && savedCallback.current(event)
    }
    for (const eventName of events) {
      on(document, eventName, handler)
    }

    return () => {
      for (const eventName of events) {
        off(document, eventName, handler)
      }
    }
  }, [events, ref])

}

export default useClickAway


